/**
 * Copyright (c) 2021 Wei-Lun Hsu. All Rights Reserved.
 */
/** @file zb32l03x_hal_cryp.c
 *
 * @author Wei-Lun Hsu
 * @version 0.1
 * @date 2021/09/27
 * @license
 * @description
 *      This file provides firmware functions to manage the following
 *      functionalities of the Cryptography (CRYP) peripheral:
 *       + Initialization and de-initialization functions
 *       + Processing functions by algorithm using polling mode
 *       + Processing functions by algorithm using interrupt mode
 *       + Processing functions by algorithm using DMA mode
 *       + Peripheral State functions
 *
   @verbatim
  ==============================================================================
                     ##### How to use this driver #####
  ==============================================================================
    [..]
      The CRYP HAL driver can be used as follows:

      (#)Initialize the CRYP low level resources by implementing the HAL_CRYP_MspInit():
         (##) Enable the CRYP interface clock using __HAL_RCC_AES_CLK_ENABLE()
         (##) In case of using interrupts (e.g. HAL_CRYP_AES_Encrypt_IT())
             (+) Configure the CRYP interrupt priority using HAL_NVIC_SetPriority()
             (+) Enable the CRYP IRQ handler using HAL_NVIC_EnableIRQ()
             (+) In CRYP IRQ handler, call HAL_CRYP_IRQHandler()

      (#)Initialize the CRYP HAL using HAL_CRYP_Init(). This function configures mainly:
         (##) The data type: 1-bit, 8-bit, 16-bit and 32-bit
         (##) The encryption/decryption key.
         (##) The initialization vector (counter). It is not used ECB mode.

      (#)Three processing (encryption/decryption) functions are available:
         (##) Polling mode: encryption and decryption APIs are blocking functions
              i.e. they process the data and wait till the processing is finished
              e.g. HAL_CRYP_AES_Encrypt()
         (##) Interrupt mode: encryption and decryption APIs are not blocking functions
              i.e. they process the data under interrupt
              e.g. HAL_CRYP_AES_Encrypt_IT()
         (##) DMA mode: encryption and decryption APIs are not blocking functions
              i.e. the data transfer is ensured by DMA
              e.g. HAL_CRYP_AES_Encrypt_DMA()

      (#)When the processing function is called for the first time after HAL_CRYP_Init()
         the CRYP peripheral is initialized and processes the buffer in input.
         At second call, the processing function performs an append of the already
         processed buffer.
         When a new data block is to be processed, call HAL_CRYP_Init() then the
         processing function.

      (#)Call HAL_CRYP_DeInit() to deinitialize the CRYP peripheral.

  @endverbatim
 */


#include "zb32l03x_hal.h"

/** @addtogroup ZB32L03x_HAL_Driver
  * @{
  */


/** @defgroup CRYP CRYP
  * @brief CRYP HAL module driver.
  * @{
  */
#if defined(HAL_CRYP_MODULE_ENABLED) && defined(CONFIG_USE_ZB32L032)

//=============================================================================
//                  Constant Definition
//=============================================================================

/**
 *  AES operating mode
 */
typedef enum
{
    AES_OPMODE_NONE     = 0,
    AES_OPMODE_ENCRYPT,
    AES_OPMODE_DECRYPT,
} aes_opmode_t;


//=============================================================================
//                  Macro Definition
//=============================================================================
#define AES_DATA_SWAP(x)            (x) // __REV(x)
//=============================================================================
//                  Structure Definition
//=============================================================================

//=============================================================================
//                  Global Data Definition
//=============================================================================

//=============================================================================
//                  Private Function Definition
//=============================================================================
/**
  * @brief  Initializes the CRYP MSP.
  * @param  hCrc:       pointer to a CRC_HandleTypeDef structure that contains
  *                     the configuration information for CRC
  * @retval None
  */
__weak void HAL_CRYP_MspInit(HAL_CRYP_Handle_TypeDef *hCryp)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hCryp);
}

/**
  * @brief  DeInitializes the CRYP MSP.
  * @param  hCrc:       pointer to a CRC_HandleTypeDef structure that contains
  *                     the configuration information for CRC
  * @retval None
  */
__weak void HAL_CRYP_MspDeInit(HAL_CRYP_Handle_TypeDef *hCryp)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hCryp);
}

/**
  * @brief  CRYP error callback.
  * @param  hCryp       pointer to a CRYP_HandleTypeDef structure that contains
  *                     the configuration information for CRYP module
  * @retval None
  */
__weak void HAL_CRYP_ErrorCallback(HAL_CRYP_Handle_TypeDef *hCryp)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hCryp);
}

/**
 * @brief  Computation completed callbacks.
 * @param  hCryp        pointer to a CRYP_HandleTypeDef structure that contains
 *                          the configuration information for CRYP module
 * @retval None
 */
__weak void HAL_CRYPE_ComputationCpltCallback(HAL_CRYP_Handle_TypeDef *hCryp)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hCryp);
}

/**
 *  @brief  Writes the Key in Key registers
 *
 *  @param [in] hCryp       Pointer to a HAL_CRYP_Handle_TypeDef structure
 *  @param [in] pKey        Pointer to Key buffer
 *  @return
 *      0: ok, others: fail
 */
static int
_AES_SetKey(HAL_CRYP_Handle_TypeDef *hCryp, uint32_t *pKey)
{
    hCryp->Instance->KEYR3 = __REV(*pKey++);
    hCryp->Instance->KEYR2 = __REV(*pKey++);
    hCryp->Instance->KEYR1 = __REV(*pKey++);
    hCryp->Instance->KEYR0 = __REV(*pKey++);
    return 0;
}


/**
 *  @brief  Writes the InitVector/InitCounter in IV registers
 *
 *  @param [in] hCryp           Pointer to a HAL_CRYP_Handle_TypeDef structure
 *  @param [in] pInitVector     Pointer to InitVector/InitCounter buffer
 *
 *  @note Init Vector must be written as little endian.
 *          If Init Vector pointer points at address n,
 *          (n +  0)[15:0] contains Vector[96:127],
 *          (n +  4)[15:0] contains Vector[64:95],
 *          (n +  8)[15:0] contains Vector[32:63] and
 *          (n + 12)[15:0] contains Vector[0:31]
 *
 *  @return                     None
 *
 */
static void
_AES_SetInitVector(
    HAL_CRYP_Handle_TypeDef     *hCryp,
    uint32_t                    *pInitVector)
{
    hCryp->Instance->IVR3 = __REV(*pInitVector++);
    hCryp->Instance->IVR2 = __REV(*pInitVector++);
    hCryp->Instance->IVR1 = __REV(*pInitVector++);
    hCryp->Instance->IVR0 = __REV(*pInitVector++);
}

/**
 *  @brief  AES Computation
 *              Writes Input data in polling mode and reads the output data
 *
 *  @param [in] hCryp           Pointer to a HAL_CRYP_Handle_TypeDef structure
 *  @param [in] pInput          Pointer to the Input buffer
 *  @param [in] pOutput         Pointer to the returned buffer
 *  @param [in] input_length    Length of the Input buffer, must be a multiple of 16.
 *  @param [in] timeout         Timeout value
 *  @return
 *      reference HAL_StatusTypeDef
 */
static HAL_StatusTypeDef
_AES_Calculate(
    HAL_CRYP_Handle_TypeDef     *hCryp,
    uint32_t                    *pInput,
    uint32_t                    *pOutput,
    uint16_t                    input_length,
    int16_t                     timeout)
{
    for(int i = 0; i < input_length; i += 16)
    {
        uint32_t        tick_start = 0ul;

        /* Write the Input block in the Data Input register */
        hCryp->Instance->DINR = AES_DATA_SWAP(*pInput++);
        hCryp->Instance->DINR = AES_DATA_SWAP(*pInput++);
        hCryp->Instance->DINR = AES_DATA_SWAP(*pInput++);
        hCryp->Instance->DINR = AES_DATA_SWAP(*pInput++);

        tick_start = HAL_GetTick();

        /* wait CRYP complete */
        while( HAL_IS_BIT_CLR(hCryp->Instance->SR, AES_SR_CCF) )
        {
            if( timeout >= 0 && (HAL_GetTick() - tick_start) > timeout )
            {
                hCryp->State = HAL_CRYP_STATE_TIMEOUT;
                return HAL_TIMEOUT;
            }
        }

        /* Clear Computation Complete Flag */
        SET_BIT(hCryp->Instance->CR, AES_CR_CCFC);

        /* Read the Output block from the Data Output Register */
        *pOutput++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
        *pOutput++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
        *pOutput++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
        *pOutput++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
    }

    return HAL_OK;
}


static HAL_StatusTypeDef
_AES_Process(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg,
    HAL_CRYP_ALGO_MODE_TypeDef      algo_mode)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    do {
        /* Check that data aligned on 32-bits and Size multiple of 16 */
        if( ((uint32_t)pCfg->pInput & 0x3ul) ||
            ((uint32_t)pCfg->pOutput & 0x3ul) ||
            (pCfg->length & 0xF) )
            break;

        if( (pCfg->cryp_mode == HAL_CRYP_MODE_CBC || pCfg->cryp_mode == HAL_CRYP_MODE_CTR) &&
            ((uint32_t)hCryp->Init.pInitVect & 0x3ul) )
            break;

        hCryp->State = HAL_CRYP_STATE_BUSY;

        /* Configure CRYP */
        if( hCryp->Phase == HAL_CRYP_PHASE_READY )
        {
            _AES_SetKey(hCryp, hCryp->Init.pKey);

            CLEAR_BIT(hCryp->Instance->CR, HAL_CRYP_ALGO_MODE_CHAIN_MASK);

            __HAL_CRYP_SET_MODE(hCryp, algo_mode);

            if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC ||
                pCfg->cryp_mode == HAL_CRYP_MODE_CTR)
            {
                _AES_SetInitVector(hCryp, hCryp->Init.pInitVect);
            }

            if( hCryp->TriggerType == HAL_CRYP_TRIGGER_POLLING )
                __HAL_CRYP_ENABLE(hCryp);

            hCryp->Phase = HAL_CRYP_PHASE_PROCESS;
        }

        /* Start Computation Process */
        if( hCryp->TriggerType == HAL_CRYP_TRIGGER_POLLING )
        {
            rval = _AES_Calculate(hCryp, pCfg->pInput, pCfg->pOutput, pCfg->length, pCfg->timeout);
            if( rval != HAL_OK )    break;
        }
        else if( hCryp->TriggerType == HAL_CRYP_TRIGGER_IRQ )
        {
            __HAL_CRYP_ENABLE_IT(hCryp, HAL_CRYP_IT_CC);
            __HAL_CRYP_ENABLE(hCryp);

            hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
            hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
            hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
            hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);

            hCryp->CrypInCount -= 16u;

            rval = HAL_OK;
        }
        else if( hCryp->TriggerType == HAL_CRYP_TRIGGER_DMA )
        {
            /* ToDo: Configure DMA */

            /* Enable In and Out DMA requests */
            SET_BIT(hCryp->Instance->CR, (AES_CR_DMAINEN | AES_CR_DMAOUTEN));

            __HAL_CRYP_ENABLE_IT(hCryp, HAL_CRYP_IT_CC);
            __HAL_CRYP_ENABLE(hCryp);

            rval = HAL_OK;
        }

        hCryp->State = HAL_CRYP_STATE_READY;
    } while(0);

    return rval;
}

/**
 *  @brief  Cryp process for interrupt
 *
 *  @param [in] hCryp       Pointer to a HAL_CRYP_Handle_TypeDef structure
 *  @return                 None
 *
 */
static void
_CRYP_IT_Process(HAL_CRYP_Handle_TypeDef *hCryp)
{
    *hCryp->pCrypOutBuffPtr++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
    *hCryp->pCrypOutBuffPtr++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
    *hCryp->pCrypOutBuffPtr++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);
    *hCryp->pCrypOutBuffPtr++ = AES_DATA_SWAP(hCryp->Instance->DOUTR);

    hCryp->CrypOutCount -= 16u;

    if( hCryp->CrypOutCount == 0u )
    {
        __HAL_CRYP_DISABLE_IT(hCryp, HAL_CRYP_IT_CC);

        hCryp->State = HAL_CRYP_STATE_READY;

        __HAL_UNLOCK(hCryp);

        HAL_CRYPE_ComputationCpltCallback(hCryp);
        return;
    }

    hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
    hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
    hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);
    hCryp->Instance->DINR = AES_DATA_SWAP(*hCryp->pCrypInBuffPtr++);

    hCryp->CrypInCount -= 16u;
    return;
}

//=============================================================================
//                  Public Function Definition
//=============================================================================
HAL_StatusTypeDef
HAL_CRYP_Init(HAL_CRYP_Handle_TypeDef *hCryp)
{
    /* Check the CRYP handle allocation */
    assert_param(hCryp);
    assert_param(hCryp->Instance);
    assert_param(!((uint32_t)hCryp->Init.pKey & 0x3ul));
    assert_param(!((uint32_t)hCryp->Init.pInitVect & 0x3ul));

    /* Check the CRYP handle */
    if( hCryp == NULL || hCryp->Instance == NULL ||
        ((uint32_t)hCryp->Init.pKey & 0x3ul) ||
        ((uint32_t)hCryp->Init.pInitVect & 0x3ul) )
    {
        return HAL_ERROR;
    }

    /* CRYP is running */
    if( HAL_IS_BIT_SET(hCryp->Instance->CR, AES_CR_EN) )
        return HAL_ERROR;

    if( hCryp->State == HAL_CRYP_STATE_RESET )
    {
        __HAL_UNLOCK(hCryp);
        HAL_CRYP_MspInit(hCryp);
    }

    hCryp->State = HAL_CRYP_STATE_BUSY;

    __HAL_RCC_AES_CLK_ENABLE();

    hCryp->CrypInCount  = 0u;
    hCryp->CrypOutCount = 0u;

    /* Set the data type*/
    MODIFY_REG(hCryp->Instance->CR, AES_CR_DATATYPE, hCryp->Init.DataType);

    hCryp->Phase = HAL_CRYP_PHASE_READY;

    /* HAL CRYP global status */
    hCryp->State = HAL_CRYP_STATE_READY;

    return HAL_OK;
}


HAL_StatusTypeDef
HAL_CRYP_DeInit(HAL_CRYP_Handle_TypeDef *hCryp)
{
    assert_param(hCryp);

    /* Check the CRYP handle */
    if( hCryp == NULL )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    __HAL_CRYP_DISABLE(hCryp);

    __HAL_RCC_AES_FORCE_RESET();
    __HAL_RCC_AES_RELEASE_RESET();

    hCryp->State = HAL_CRYP_STATE_BUSY;

    hCryp->Phase = HAL_CRYP_PHASE_READY;

    __HAL_CRYP_DISABLE_IT(hCryp, HAL_CRYP_IT_CC);
    __HAL_CRYP_DISABLE_IT(hCryp, HAL_CRYP_IT_ERR);

    HAL_CRYP_MspDeInit(hCryp);

    __HAL_RCC_AES_CLK_DISABLE();

    hCryp->State = HAL_CRYP_STATE_RESET;

    __HAL_UNLOCK(hCryp);

    return HAL_OK;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Encrypt(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        if( hCryp->State != HAL_CRYP_STATE_READY )
            break;

        hCryp->TriggerType = HAL_CRYP_TRIGGER_POLLING;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_ENCRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);
    return rval;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Decrypt(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Decrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        if( hCryp->State != HAL_CRYP_STATE_READY )
            break;

        hCryp->TriggerType = HAL_CRYP_TRIGGER_POLLING;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_DECRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);
    return rval;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Encrypt_IT(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        hCryp->pCrypInBuffPtr   = pCfg->pInput;
        hCryp->pCrypOutBuffPtr  = pCfg->pOutput;
        hCryp->CrypInCount      = pCfg->length;
        hCryp->CrypOutCount     = pCfg->length;
        hCryp->TriggerType      = HAL_CRYP_TRIGGER_IRQ;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_ENCRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);

    return rval;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Decrypt_IT(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        hCryp->pCrypInBuffPtr   = pCfg->pInput;
        hCryp->pCrypOutBuffPtr  = pCfg->pOutput;
        hCryp->CrypInCount      = pCfg->length;
        hCryp->CrypOutCount     = pCfg->length;
        hCryp->TriggerType      = HAL_CRYP_TRIGGER_IRQ;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_DECRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);

    return rval;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Encrypt_DMA(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        hCryp->TriggerType = HAL_CRYP_TRIGGER_DMA;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_ENCRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_ENCRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);

    return rval;
}

HAL_StatusTypeDef
HAL_CRYP_AES_Decrypt_DMA(
    HAL_CRYP_Handle_TypeDef         *hCryp,
    HAL_CRYP_Encrypt_Cfg_TypeDef    *pCfg)
{
    HAL_StatusTypeDef   rval = HAL_ERROR;

    assert_param(hCryp);
    assert_param(pCfg);
    assert_param(pCfg->pInput);
    assert_param(pCfg->pOutput);
    assert_param(pCfg->length);

    if( !hCryp || !pCfg || !pCfg->length ||
        !pCfg->pInput || !pCfg->pOutput )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    do {
        HAL_CRYP_ALGO_MODE_TypeDef      algo_mode;

        hCryp->TriggerType = HAL_CRYP_TRIGGER_DMA;

        if( pCfg->cryp_mode == HAL_CRYP_MODE_ECB )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_ECB_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CBC )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CBC_KEYDERDECRYPT;
        else if( pCfg->cryp_mode == HAL_CRYP_MODE_CTR )
            algo_mode = HAL_CRYP_ALGO_MODE_AES_CTR_DECRYPT;
        else        break;

        rval = _AES_Process(hCryp, pCfg, algo_mode);

    } while(0);

    __HAL_UNLOCK(hCryp);

    return rval;
}


HAL_StatusTypeDef
HAL_CRYP_AES_Key_Derivation(
    HAL_CRYP_Handle_TypeDef     *hCryp,
    uint32_t                    *pKey,
    uint32_t                    *pKeyDerived,
    int                         key_length)
{
    HAL_StatusTypeDef   rval = HAL_OK;
    uint32_t            tick_start = 0;

    assert_param(hCryp);
    assert_param(pKey);
    assert_param(pKeyDerived);
    assert_param(key_length >= 16);

    if( !hCryp || !pKey || !pKeyDerived )
        return HAL_ERROR;

    if( ((uint32_t)pKey & 0x3ul) ||
        ((uint32_t)pKeyDerived & 0x3ul) ||
        key_length < 16 )
        return HAL_ERROR;

    __HAL_LOCK(hCryp);

    CLEAR_BIT(hCryp->Instance->CR, HAL_CRYP_ALGO_MODE_CHAIN_MASK);

    __HAL_CRYP_SET_MODE(hCryp, (AES_CR_MODE_MODE_2 << AES_CR_MODE_Pos) | (AES_CR_CHMOD_RSVD << AES_CR_CHMOD_Pos));

    {
        _AES_SetKey(hCryp, pKey);

        __HAL_CRYP_ENABLE(hCryp);

        tick_start = HAL_GetTick();

        /* wait CRYP complete */
        while( HAL_IS_BIT_CLR(hCryp->Instance->SR, AES_SR_CCF) )
        {
            if( (HAL_GetTick() - tick_start) > 1000 )
            {
                hCryp->State = HAL_CRYP_STATE_TIMEOUT;
                rval = HAL_TIMEOUT;
                break;
            }
        }

        if( rval == HAL_OK )
        {
            SET_BIT(hCryp->Instance->CR, AES_CR_CCFC);

            /* get keys */
            *pKeyDerived++ = __REV(hCryp->Instance->KEYR3);
            *pKeyDerived++ = __REV(hCryp->Instance->KEYR2);
            *pKeyDerived++ = __REV(hCryp->Instance->KEYR1);
            *pKeyDerived++ = __REV(hCryp->Instance->KEYR0);
        }
    }

    __HAL_UNLOCK(hCryp);

    return rval;
}


void HAL_CRYP_IRQHandler(HAL_CRYP_Handle_TypeDef *hCryp)
{
    assert_param(hCryp);

    if( __HAL_CRYP_GET_IT_FLAG(hCryp, HAL_CRYP_FLAG_RDERR | HAL_CRYP_FLAG_WRERR) )
    {
        /* Check if error occurred */
        __HAL_CRYP_CLEAR_FLAG(hCryp, HAL_CRYP_CLRFLAG_ERR);
        __HAL_CRYP_CLEAR_FLAG(hCryp, HAL_CRYP_CLRFLAG_CCF);

        hCryp->State = HAL_CRYP_STATE_ERROR;

        __HAL_CRYP_DISABLE_IT(hCryp, HAL_CRYP_IT_CC);
        __HAL_CRYP_DISABLE_IT(hCryp, HAL_CRYP_IT_ERR);

        __HAL_CRYP_DISABLE(hCryp);

        HAL_CRYP_ErrorCallback(hCryp);
        __HAL_UNLOCK(hCryp);
    }
    else if( __HAL_CRYP_GET_IT_FLAG(hCryp, HAL_CRYP_FLAG_CCF) )
    {
        /* Check if computation complete interrupt was enabled */
        __HAL_CRYP_CLEAR_FLAG(hCryp, HAL_CRYP_CLRFLAG_CCF);

        if( hCryp->TriggerType == HAL_CRYP_TRIGGER_DMA )
        {
            hCryp->State = HAL_CRYP_STATE_READY;

            HAL_CRYPE_ComputationCpltCallback(hCryp);
        }
        else
            _CRYP_IT_Process(hCryp);
    }
}

#endif

/**
  * @}
  */ /* group CRYP CRYP */

/**
  * @}
  */ /* group ZB32L03x_HAL_Driver */
